/**
 * Copyright (c) 2012, Brent Mucci, Ken Anderson
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

#include <sstream>
#include <vector>

#include "mcl_common.h"
#include "mcl_image.h"
#include "mcl_bitmap.h"
#include "mcl_timer.h"
#include "mcl_visualizer.h"
#include "mcl_image_resize.h"
#include "mcl_image_resize_bilinear.h"
#include "mcl_image_resize_bilinearCL.h"

// This program loads or generates bitmap images,
// then resizes them and displays them on the screen.
// While displaying timing statistics.
// The point of this is to compare the speed of resizing algorithms
// with and without OpenCL enabled.

#define LOAD_IMAGES true  // Either load images (if they exist), or use a generated checkerboard pattern
#define WINDOW_WIDTH 800    // Width of visualization window
#define WINDOW_HEIGHT 600   // Height of visualization window
#define WINDOW_CELLS 2      // sqrt of the number of cells in the window.
#define NUM_IMAGES 90       // How many images to load/generate (up to 90)


// Many images may be displayed to the window. If the window displays more than 1 image then it is stored in 1 'cell.
// More cells are needed if more videos/images are displayed to a single window. Each cell is equal in dimensions.
const int CELL_WIDTH  = WINDOW_WIDTH / WINDOW_CELLS; // The width of each cell (cell widths are all equal)
const int CELL_HEIGHT = WINDOW_HEIGHT / WINDOW_CELLS; // The height of each cell (cell heights are all equal)
const int NUM_CELLS   = WINDOW_CELLS * WINDOW_CELLS; // The number of cells to be displayed within a window/screen.
// Chronological ordering of frames are sorted through incrementing a number that is used in the file name.
// FIRST_IMG is the number to start incrementing from.
const int FIRST_IMG   = 1865;

using namespace mcl;

// Returns the filename of the image file.
std::string getFilename(int index)
{
	std::stringstream bigbuckbunnyLocation;
	bigbuckbunnyLocation << INPUT_DIR DDEL "bitmap" DDEL "big_buck_bunny_0";
	bigbuckbunnyLocation << (FIRST_IMG + index);
	bigbuckbunnyLocation << ".BMP";
	return bigbuckbunnyLocation.str();
}

int main()
{
	// Initialize the visualizer to output the graphics to the screen.
	Visualizer viz(WINDOW_WIDTH,WINDOW_HEIGHT,WINDOW_CELLS);
	// Initialize a vector of images to be filtered and/or resampled (resized)
	std::vector<Image> images(NUM_IMAGES);
	// This will store the frame once it has been resized.
	Image resizedImage;
	
	
#ifdef USE_OPENCL
	// Use ImageResizeBilinearCL for OpenCL acceleration (GPU)
	ImageResizeBilinearCL imageResizer; 
#else
	// Use ImageResizeBilinear that uses the CPU
	ImageResizeBilinear imageResizer; 
#endif

	// Initializes some basic parameters of the class that will store the resized image/frame.
	resizedImage.init(CELL_WIDTH,CELL_HEIGHT);

	// Load or generate images
	for(int i = 0; i < NUM_IMAGES; i++){
		std::stringstream loadingStatus;

		// *** IF YOU SEE A CHECKERBOARD MAKE CERTAIN THIS CONSTANT IS SET TO TRUE ***
		if( LOAD_IMAGES )
		{
			std::string filename = getFilename(i);
			BitmapLoader loader;
			loader.load( filename, images[i] );
			images[i].flipY();
		}
		else
		{
			images[i].init(1920,1080);
			images[i].generateCheckerboard(i);	// Generate checkerboards
		}

		viz.handleEvents();
		viz.clear();
		loadingStatus << "Loading: " << (int)((((float)i)/(NUM_IMAGES-1)) * 100) << "%";
		viz.print(loadingStatus.str());
		viz.display();
	}

	viz.clear();
	viz.print("Initializing...");
	viz.display();

	// Run Test
	unsigned int cellRow = 0;
	unsigned int cellCol = 0;
	unsigned int totalCells = WINDOW_CELLS * WINDOW_CELLS;
	unsigned int currentFrame = 0;
	double fps;
	double totalFps;
	Timer timer;

	// Loop until ESC
	while ( viz.isOpened() )
	{
		// Handle events
		viz.handleEvents();

		// Clear the secondary buffer in the double buffer
		viz.clear();

		// Render the new frame in all of the cells inside of the window
		totalFps = 0;
		for(unsigned int x=0; x<WINDOW_CELLS; x++)
		{
			for( unsigned int y=0; y<WINDOW_CELLS; y++)
			{
				// Resize the image on the fly using only the CPU. Do this for each cell or "channel"
				timer.start();
				imageResizer.resize(images[currentFrame], resizedImage);
				const double seconds = timer.getSeconds();

				fps = 1.0 / seconds;
				totalFps += fps;
				viz.drawImage(resizedImage,x,y);
			}
		}
		if(NUM_CELLS > 0){
			totalFps = totalFps / NUM_CELLS;
		}

		std::stringstream output;
		output << "Maximum FPS: " << totalFps;
		viz.print(output.str());

		// Swap buffers in the double buffer to render the image to the screen
		viz.display();
		
		currentFrame++;
		if(currentFrame == NUM_IMAGES)
		{	// Reset
			currentFrame = 0;
		}
	}

	return EXIT_SUCCESS;
}
